package checker

import (
	"mi/compiler/binder"
	"reflect"
)

func (c *checker) typeGetter(target interface{}) interface{} {
	return c.getSymbolLinks(target.(*binder.Symbol)).tpe
}

func (c *checker) declarationTypeGetter(target interface{}) interface{} {
	return c.getSymbolLinks(target.(*binder.Symbol)).declarationType
}

func (c *checker) signatureResolvedReturnTypeGetter(target interface{}) interface{} {
	return target.(*Signature).ResolvedReturnType
}

type getterFunc func(target interface{}) interface{}

// Push an entry on the type resolution stack. If an entry with the given target and the given property name
// is already on the stack, and no entries in between already have a type, then a circularity has occurred.
// In this case, the result values of the existing entry and all entries pushed after it are changed to false,
// and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned.
// In order to see if the same query has already been done before, the target object and the propertyName both
// must match the one passed in.
//
// param target The symbol, type, or signature whose type is being queried
// param propertyName The property name that should be used to query the target for its type
func (c *checker) pushResolution(target interface{}, getter getterFunc) bool {
	assert(target != nil)
	var resolutionCycleStartIndex = c.findResolutionCycleStartIndex(target, getter)
	if resolutionCycleStartIndex >= 0 {
		// A cycle was found
		var length = len(c.resolutionTargets)
		for i := resolutionCycleStartIndex; i < length; i++ {
			c.resolutionResults[i] = false
		}
		return false
	}
	c.resolutionTargets = append(c.resolutionTargets, target)
	c.resolutionResults = append(c.resolutionResults, true)
	c.resolutionGetters = append(c.resolutionGetters, getter)
	return true
}

func (c *checker) getterFuncEquals(f1 getterFunc, f2 getterFunc) bool {
	var sf1 = reflect.ValueOf(f1)
	var sf2 = reflect.ValueOf(f2)
	return sf1.Pointer() == sf2.Pointer()
}

func (c *checker) findResolutionCycleStartIndex(target interface{}, getter getterFunc) int {
	for i := len(c.resolutionTargets) - 1; i >= 0; i-- {
		if !isNull(c.resolutionGetters[i](c.resolutionTargets[i])) {
			return -1
		}
		if c.resolutionTargets[i] == target && c.getterFuncEquals(c.resolutionGetters[i], getter) {
			return i
		}
	}

	return -1
}

// Pop an entry from the type resolution stack and return its associated result value. The result value will
// be true if no circularities were detected, or false if a circularity was found.
func (c *checker) popResolution() bool {
	if len(c.resolutionTargets) > 0 {
		c.resolutionTargets = c.resolutionTargets[:len(c.resolutionTargets)-1]
	}
	if len(c.resolutionGetters) > 0 {
		c.resolutionGetters = c.resolutionGetters[:len(c.resolutionGetters)-1]
	}
	if len(c.resolutionResults) > 0 {
		c.resolutionResults = c.resolutionResults[:len(c.resolutionResults)-1]
		return true
	}
	return false
}